package com.iwomy.envpro.platform.handler;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.fastjson.JSONObject;
import com.iwomy.envpro.platform.constant.DeviceStatusConstants;
import com.iwomy.envpro.platform.constant.HJ212Constants;
import com.iwomy.envpro.platform.constant.RedisConstants;
import com.iwomy.envpro.platform.constant.TpEnum;
import com.iwomy.envpro.platform.dto.DeviceDataDTO;
import com.iwomy.envpro.platform.entity.DeviceLogEntity;
import com.iwomy.envpro.platform.exception.PlatformException;
import com.iwomy.envpro.platform.service.DeviceLogService;
import com.iwomy.envpro.platform.service.DeviceService;
import com.iwomy.envpro.platform.utils.HJ212MsgUtils;
import com.iwomy.envpro.platform.utils.RedisClient;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleStateEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;


/**
 * @author ruanwb
 * @Description 环保设备数据处理类，处理设备的上传数据
 * @Date 2023-9-28 17:44:05
 **/
@Slf4j
@Component
public class EPServerHandler extends ChannelInboundHandlerAdapter {

	/**
	 * 定义一个HashMap，用于保存所有的channel和设备ID的对应关系。
	 */
	private static Map<String, String> deviceMnById = new HashMap(64);
	private static Map<String, ChannelHandlerContext> ctxByMn = new HashMap(64);
	private final DeviceService deviceService = SpringUtil.getBean(DeviceService.class);
	private final RedisClient redisClient = SpringUtil.getBean(RedisClient.class);
	private final DeviceLogService deviceLogService = SpringUtil.getBean(DeviceLogService.class);

	/**
	 * 消息读取
	 */
	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) {
//        log.info("=============" + (new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss")).format(new Date()) + "=============");
//        log.info("收到" + ctx.channel().id() + "设备消息 ===> " + msg);
		String id = ctx.channel().id().toString();
		JSONObject data2 = null;
		try {
			data2 = HJ212MsgUtils.dealMsg2((String) msg);
		} catch (Exception e) {
			log.error("收到{},设备消息 ===> {},{}", id, msg, e.getMessage());
			return;
		}


		if (ObjectUtil.isNull(data2)) {
			log.info("收到{},设备消息 ===>{}", id, msg);
			return;
		}
		String mn = data2.getString("MN");
		if (StrUtil.isEmpty(deviceMnById.get(id)) && StrUtil.isNotEmpty(mn)) {

			deviceMnById.put(id, mn);
			ctxByMn.put(mn, ctx);
			deviceService.deviceOnline(mn, DeviceStatusConstants.DEVICE_ONLINE);
		}
		mn = deviceMnById.get(id);
		if (StrUtil.isNotEmpty(mn)) {
			DeviceLogEntity deviceLogEntity = new DeviceLogEntity();
			deviceLogEntity.setEquipmentCode(mn);
			deviceLogEntity.setContent(msg.toString());
			deviceLogService.save(deviceLogEntity);
		} else {
			return;
		}

		// 解析物联网设备发来的数据
//        JSONObject data = HJ212MsgUtils.dealMsg1((String) msg);
		String content = data2.toJSONString();
		DeviceDataDTO deviceDataDTO = JSONObject.parseObject(content, DeviceDataDTO.class);
		if (ObjectUtil.isNull(deviceDataDTO.getCn())) {
			redisClient.sSet(RedisConstants.ENVPRO_DEVICS_CONFIG_KEY + mn, msg);
			log.info("收到{}设备消息 ===>{} ", mn, msg);
			return;
		}
		switch (deviceDataDTO.getCn()) {
			case HJ212Constants.COMMAND_CODE_UPLOAD_REAL_DATA:
				deviceService.escalation(deviceDataDTO, TpEnum.REAl_TIME);
				break;
			case HJ212Constants.COMMAND_CODE_UPLOAD_MINUTELY_DATA:
				deviceService.escalation(deviceDataDTO, TpEnum.MINUTELY_AVG);
				break;
			case HJ212Constants.COMMAND_CODE_UPLOAD_HOUR_DATA:
				deviceService.escalation(deviceDataDTO, TpEnum.HOURLY_AVG);
				break;
			case HJ212Constants.COMMAND_CODE_UPLOAD_DAY_DATA:
				deviceService.escalation(deviceDataDTO, TpEnum.DAILY_AVG);
				break;
			default:
				log.info("解析物联网设备发来的数据:" + msg);
		}
		/**
		 * 做自己的业务逻辑，分发数据，分析数据，持久化数据等。
		 */

	}

	/***
	 * 超时关闭socket 连接
	 */
	@Override
	public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
		if (evt instanceof IdleStateEvent) {
			IdleStateEvent event = (IdleStateEvent) evt;

			String eventType = null;
			switch (event.state()) {
				case READER_IDLE:
					eventType = "读超时";
					break;
				case WRITER_IDLE:
					eventType = "写超时";
					break;
				case ALL_IDLE:
					eventType = "读写超时";
					break;
				default:
					eventType = "设备超时";
			}
			log.warn(ctx.channel().id() + " : " + eventType + "---> 关闭该设备");
			this.removeDevice(ctx);
		}
	}

	/**
	 * 异常处理, 出现异常关闭channel
	 */
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
		log.error("========= 链接出现异常！exceptionCaught.");
		ctx.fireExceptionCaught(cause);
		this.removeDevice(ctx);
	}

	/**
	 * 每加入一个新的链接，保存该通道并写入上线日志。该方法在channelRead方法之前执行
	 */
	@Override
	public void handlerAdded(ChannelHandlerContext ctx) {
		// 获取设备ID
		// 将该链接保存到deviceMnById
		log.warn("========= " + ctx.channel().id() + "设备加入链接。");
	}

	/**
	 * 每去除一个新的链接，去除该通道并写入下线日志
	 */
	@Override
	public void handlerRemoved(ChannelHandlerContext ctx) {
		// 将通道从deviceMnById中删除
		this.removeDevice(ctx);
	}

	public static void sendCommand(String mn, String command) {
		ChannelHandlerContext channelHandlerContext = ctxByMn.get(mn);
		if (ObjectUtil.isNull(channelHandlerContext)) {
			log.error("sendCommand设备不存在");
			throw new PlatformException("设备不存在");
		}
		log.info("sendCommand:{}", command);
		channelHandlerContext.writeAndFlush(command).addListener((ChannelFutureListener) future -> {
			if (future.isSuccess()) {
				// 写入操作成功
				log.info("Data sent successfully!");
			} else {
				// 写入操作失败
				log.error("Failed to send data: " + future.cause().getMessage());
			}
		});
	}

	private void removeDevice(ChannelHandlerContext ctx) {
		String id = ctx.channel().id().toString();
		String mn = deviceMnById.get(id);
		ctx.channel().close();
		log.warn("========= " + ctx.channel().id() + "======" + mn + "设备断开链接。");
		deviceMnById.remove(id);
		ctxByMn.remove(mn);
		deviceService.deviceOnline(mn, DeviceStatusConstants.DEVICE_OFFLINE);
	}

}
